home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / PIL / ImageDraw.py < prev    next >
Text File  |  2006-12-03  |  12KB  |  379 lines

  1. #
  2. # The Python Imaging Library
  3. # $Id: ImageDraw.py 2817 2006-10-07 15:34:03Z fredrik $
  4. #
  5. # drawing interface operations
  6. #
  7. # History:
  8. # 1996-04-13 fl   Created (experimental)
  9. # 1996-08-07 fl   Filled polygons, ellipses.
  10. # 1996-08-13 fl   Added text support
  11. # 1998-06-28 fl   Handle I and F images
  12. # 1998-12-29 fl   Added arc; use arc primitive to draw ellipses
  13. # 1999-01-10 fl   Added shape stuff (experimental)
  14. # 1999-02-06 fl   Added bitmap support
  15. # 1999-02-11 fl   Changed all primitives to take options
  16. # 1999-02-20 fl   Fixed backwards compatibility
  17. # 2000-10-12 fl   Copy on write, when necessary
  18. # 2001-02-18 fl   Use default ink for bitmap/text also in fill mode
  19. # 2002-10-24 fl   Added support for CSS-style color strings
  20. # 2002-12-10 fl   Added experimental support for RGBA-on-RGB drawing
  21. # 2002-12-11 fl   Refactored low-level drawing API (work in progress)
  22. # 2004-08-26 fl   Made Draw() a factory function, added getdraw() support
  23. # 2004-09-04 fl   Added width support to line primitive
  24. # 2004-09-10 fl   Added font mode handling
  25. # 2006-06-19 fl   Added font bearing support (getmask2)
  26. #
  27. # Copyright (c) 1997-2006 by Secret Labs AB
  28. # Copyright (c) 1996-2006 by Fredrik Lundh
  29. #
  30. # See the README file for information on usage and redistribution.
  31. #
  32.  
  33. import Image, ImageColor
  34.  
  35. try:
  36.     import warnings
  37. except ImportError:
  38.     warnings = None
  39.  
  40. ##
  41. # A simple 2D drawing interface for PIL images.
  42. # <p>
  43. # Application code should use the <b>Draw</b> factory, instead of
  44. # directly.
  45.  
  46. class ImageDraw:
  47.  
  48.     ##
  49.     # Create a drawing instance.
  50.     #
  51.     # @param im The image to draw in.
  52.     # @param mode Optional mode to use for color values.  For RGB
  53.     #    images, this argument can be RGB or RGBA (to blend the
  54.     #    drawing into the image).  For all other modes, this argument
  55.     #    must be the same as the image mode.  If omitted, the mode
  56.     #    defaults to the mode of the image.
  57.  
  58.     def __init__(self, im, mode=None):
  59.         im.load()
  60.         if im.readonly:
  61.             im._copy() # make it writable
  62.         blend = 0
  63.         if mode is None:
  64.             mode = im.mode
  65.         if mode != im.mode:
  66.             if mode == "RGBA" and im.mode == "RGB":
  67.                 blend = 1
  68.             else:
  69.                 raise ValueError("mode mismatch")
  70.         if mode == "P":
  71.             self.palette = im.palette
  72.         else:
  73.             self.palette = None
  74.         self.im = im.im
  75.         self.draw = Image.core.draw(self.im, blend)
  76.         self.mode = mode
  77.         if mode in ("I", "F"):
  78.             self.ink = self.draw.draw_ink(1, mode)
  79.         else:
  80.             self.ink = self.draw.draw_ink(-1, mode)
  81.         if mode in ("1", "P", "I", "F"):
  82.             # FIXME: fix Fill2 to properly support matte for I+F images
  83.             self.fontmode = "1"
  84.         else:
  85.             self.fontmode = "L" # aliasing is okay for other modes
  86.         self.fill = 0
  87.         self.font = None
  88.  
  89.     ##
  90.     # Set the default pen color.
  91.  
  92.     def setink(self, ink):
  93.         # compatibility
  94.         if warnings:
  95.             warnings.warn(
  96.                 "'setink' is deprecated; use keyword arguments instead",
  97.                 DeprecationWarning, stacklevel=2
  98.                 )
  99.         if Image.isStringType(ink):
  100.             ink = ImageColor.getcolor(ink, self.mode)
  101.         if self.palette and not Image.isNumberType(ink):
  102.             ink = self.palette.getcolor(ink)
  103.         self.ink = self.draw.draw_ink(ink, self.mode)
  104.  
  105.     ##
  106.     # Set the default background color.
  107.  
  108.     def setfill(self, onoff):
  109.         # compatibility
  110.         if warnings:
  111.             warnings.warn(
  112.                 "'setfill' is deprecated; use keyword arguments instead",
  113.                 DeprecationWarning, stacklevel=2
  114.                 )
  115.         self.fill = onoff
  116.  
  117.     ##
  118.     # Set the default font.
  119.  
  120.     def setfont(self, font):
  121.         # compatibility
  122.         self.font = font
  123.  
  124.     ##
  125.     # Get the current default font.
  126.  
  127.     def getfont(self):
  128.         if not self.font:
  129.             # FIXME: should add a font repository
  130.             import ImageFont
  131.             self.font = ImageFont.load_default()
  132.         return self.font
  133.  
  134.     def _getink(self, ink, fill=None):
  135.         if ink is None and fill is None:
  136.             if self.fill:
  137.                 fill = self.ink
  138.             else:
  139.                 ink = self.ink
  140.         else:
  141.             if ink is not None:
  142.                 if Image.isStringType(ink):
  143.                     ink = ImageColor.getcolor(ink, self.mode)
  144.                 if self.palette and not Image.isNumberType(ink):
  145.                     ink = self.palette.getcolor(ink)
  146.                 ink = self.draw.draw_ink(ink, self.mode)
  147.             if fill is not None:
  148.                 if Image.isStringType(fill):
  149.                     fill = ImageColor.getcolor(fill, self.mode)
  150.                 if self.palette and not Image.isNumberType(fill):
  151.                     fill = self.palette.getcolor(fill)
  152.                 fill = self.draw.draw_ink(fill, self.mode)
  153.         return ink, fill
  154.  
  155.     ##
  156.     # Draw an arc.
  157.  
  158.     def arc(self, xy, start, end, fill=None):
  159.         ink, fill = self._getink(fill)
  160.         if ink is not None:
  161.             self.draw.draw_arc(xy, start, end, ink)
  162.  
  163.     ##
  164.     # Draw a bitmap.
  165.  
  166.     def bitmap(self, xy, bitmap, fill=None):
  167.         bitmap.load()
  168.         ink, fill = self._getink(fill)
  169.         if ink is None:
  170.             ink = fill
  171.         if ink is not None:
  172.             self.draw.draw_bitmap(xy, bitmap.im, ink)
  173.  
  174.     ##
  175.     # Draw a chord.
  176.  
  177.     def chord(self, xy, start, end, fill=None, outline=None):
  178.         ink, fill = self._getink(outline, fill)
  179.         if fill is not None:
  180.             self.draw.draw_chord(xy, start, end, fill, 1)
  181.         if ink is not None:
  182.             self.draw.draw_chord(xy, start, end, ink, 0)
  183.  
  184.     ##
  185.     # Draw an ellipse.
  186.  
  187.     def ellipse(self, xy, fill=None, outline=None):
  188.         ink, fill = self._getink(outline, fill)
  189.         if fill is not None:
  190.             self.draw.draw_ellipse(xy, fill, 1)
  191.         if ink is not None:
  192.             self.draw.draw_ellipse(xy, ink, 0)
  193.  
  194.     ##
  195.     # Draw a line, or a connected sequence of line segments.
  196.  
  197.     def line(self, xy, fill=None, width=0):
  198.         ink, fill = self._getink(fill)
  199.         if ink is not None:
  200.             self.draw.draw_lines(xy, ink, width)
  201.  
  202.     ##
  203.     # (Experimental) Draw a shape.
  204.  
  205.     def shape(self, shape, fill=None, outline=None):
  206.         # experimental
  207.         shape.close()
  208.         ink, fill = self._getink(outline, fill)
  209.         if fill is not None:
  210.             self.draw.draw_outline(shape, fill, 1)
  211.         if ink is not None:
  212.             self.draw.draw_outline(shape, ink, 0)
  213.  
  214.     ##
  215.     # Draw a pieslice.
  216.  
  217.     def pieslice(self, xy, start, end, fill=None, outline=None):
  218.         ink, fill = self._getink(outline, fill)
  219.         if fill is not None:
  220.             self.draw.draw_pieslice(xy, start, end, fill, 1)
  221.         if ink is not None:
  222.             self.draw.draw_pieslice(xy, start, end, ink, 0)
  223.  
  224.     ##
  225.     # Draw one or more individual pixels.
  226.  
  227.     def point(self, xy, fill=None):
  228.         ink, fill = self._getink(fill)
  229.         if ink is not None:
  230.             self.draw.draw_points(xy, ink)
  231.  
  232.     ##
  233.     # Draw a polygon.
  234.  
  235.     def polygon(self, xy, fill=None, outline=None):
  236.         ink, fill = self._getink(outline, fill)
  237.         if fill is not None:
  238.             self.draw.draw_polygon(xy, fill, 1)
  239.         if ink is not None:
  240.             self.draw.draw_polygon(xy, ink, 0)
  241.  
  242.     ##
  243.     # Draw a rectangle.
  244.  
  245.     def rectangle(self, xy, fill=None, outline=None):
  246.         ink, fill = self._getink(outline, fill)
  247.         if fill is not None:
  248.             self.draw.draw_rectangle(xy, fill, 1)
  249.         if ink is not None:
  250.             self.draw.draw_rectangle(xy, ink, 0)
  251.  
  252.     ##
  253.     # Draw text.
  254.  
  255.     def text(self, xy, text, fill=None, font=None, anchor=None):
  256.         ink, fill = self._getink(fill)
  257.         if font is None:
  258.             font = self.getfont()
  259.         if ink is None:
  260.             ink = fill
  261.         if ink is not None:
  262.             try:
  263.                 mask, offset = font.getmask2(text, self.fontmode)
  264.                 xy = xy[0] + offset[0], xy[1] + offset[1]
  265.             except AttributeError:
  266.                 try:
  267.                     mask = font.getmask(text, self.fontmode)
  268.                 except TypeError:
  269.                     mask = font.getmask(text)
  270.             self.draw.draw_bitmap(xy, mask, ink)
  271.  
  272.     ##
  273.     # Get the size of a given string, in pixels.
  274.  
  275.     def textsize(self, text, font=None):
  276.         if font is None:
  277.             font = self.getfont()
  278.         return font.getsize(text)
  279.  
  280. ##
  281. # A simple 2D drawing interface for PIL images.
  282. #
  283. # @param im The image to draw in.
  284. # @param mode Optional mode to use for color values.  For RGB
  285. #    images, this argument can be RGB or RGBA (to blend the
  286. #    drawing into the image).  For all other modes, this argument
  287. #    must be the same as the image mode.  If omitted, the mode
  288. #    defaults to the mode of the image.
  289.  
  290. def Draw(im, mode=None):
  291.     try:
  292.         return im.getdraw(mode)
  293.     except AttributeError:
  294.         return ImageDraw(im, mode)
  295.  
  296. # experimental access to the outline API
  297. try:
  298.     Outline = Image.core.outline
  299. except:
  300.     Outline = None
  301.  
  302. ##
  303. # (Experimental) A more advanced 2D drawing interface for PIL images,
  304. # based on the WCK interface.
  305. #
  306. # @param im The image to draw in.
  307. # @param hints An optional list of hints.
  308. # @return A (drawing context, drawing resource factory) tuple.
  309.  
  310. def getdraw(im=None, hints=None):
  311.     # FIXME: this needs more work!
  312.     # FIXME: come up with a better 'hints' scheme.
  313.     handler = None
  314.     if not hints or "nicest" in hints:
  315.         try:
  316.             import _imagingagg
  317.             handler = _imagingagg
  318.         except ImportError:
  319.             pass
  320.     if handler is None:
  321.         import ImageDraw2
  322.         handler = ImageDraw2
  323.     if im:
  324.         im = handler.Draw(im)
  325.     return im, handler
  326.  
  327. ##
  328. # (experimental) Fills a bounded region with a given color.
  329. #
  330. # @param image Target image.
  331. # @param xy Seed position (a 2-item coordinate tuple).
  332. # @param value Fill color.
  333. # @param border Optional border value.  If given, the region consists of
  334. #     pixels with a color different from the border color.  If not given,
  335. #     the region consists of pixels having the same color as the seed
  336. #     pixel.
  337.  
  338. def floodfill(image, xy, value, border=None):
  339.     "Fill bounded region."
  340.     # based on an implementation by Eric S. Raymond
  341.     pixel = image.load()
  342.     x, y = xy
  343.     try:
  344.         background = pixel[x, y]
  345.         if background == value:
  346.             return # seed point already has fill color
  347.         pixel[x, y] = value
  348.     except IndexError:
  349.         return # seed point outside image
  350.     edge = [(x, y)]
  351.     if border is None:
  352.         while edge:
  353.             newedge = []
  354.             for (x, y) in edge:
  355.                 for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
  356.                     try:
  357.                         p = pixel[s, t]
  358.                     except IndexError:
  359.                         pass
  360.                     else:
  361.                         if p == background:
  362.                             pixel[s, t] = value
  363.                             newedge.append((s, t))
  364.             edge = newedge
  365.     else:
  366.         while edge:
  367.             newedge = []
  368.             for (x, y) in edge:
  369.                 for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
  370.                     try:
  371.                         p = pixel[s, t]
  372.                     except IndexError:
  373.                         pass
  374.                     else:
  375.                         if p != value and p != border:
  376.                             pixel[s, t] = value
  377.                             newedge.append((s, t))
  378.             edge = newedge
  379.